package com.jtw.sys.util;

import com.jtw.common.util.DateUtil;
import com.jtw.common.util.SpringUtil;
import com.jtw.sys.model.log.TSysTaskLog;
import com.jtw.sys.service.log.SysTaskLogServiceImpl;
import com.jtw.sys.vo.task.TaskModelVo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.CronTriggerImpl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * DESCRIPT: 定时任务辅助类
 *
 * @author cjsky666
 * @date 2018/11/23 14:22
 */
@Slf4j
public class TaskUtil {
    private static Object lock = new Object();//定义一个存储锁，防止task在进行增删改的时候出现task线程操作数据的错误
    private static SysTaskLogServiceImpl sysTaskLogService= SpringUtil.getBean(SysTaskLogServiceImpl.class);
    private static List<TSysTaskLog> logList = new ArrayList<>();
    /**
     * 通过反射调用taskModel中定义的方法
     *
     * @param taskModelVo
     */
    public static void invokMethod(TaskModelVo taskModelVo) {
        log.info(taskModelVo.toString());
        Object object = null;
        Class clazz = null;
        boolean flag = true;
        if (StringUtils.isNotBlank(taskModelVo.getBeanClass())) {
            try {
                clazz = Class.forName(taskModelVo.getBeanClass());
                object = clazz.newInstance();
            } catch (Exception e) {
                flag = false;
                log.error("未找到"+ taskModelVo.getBeanClass()+"对应的class");
                saveTaskLog(taskModelVo.getId(), taskModelVo.getName(), taskModelVo.getBeanClass(),flag,taskModelVo.getGameId(),"未找到"+ taskModelVo.getBeanClass()+"对应的class");
                e.printStackTrace();
            }
        }

        clazz = object.getClass();
        Method method = null;
        try {
            method = clazz.getDeclaredMethod(taskModelVo.getMethodName(), new Class[]{String.class,Integer.class});
        } catch (NoSuchMethodException e) {
            flag = false;
            log.error("任务名称 = [" + taskModelVo.getName() + "]---------------启动失败，方法名设置错误！！！");
            saveTaskLog(taskModelVo.getId(), taskModelVo.getName(), taskModelVo.getBeanClass(),flag,taskModelVo.getGameId(),"任务名称 = [" + taskModelVo.getName() + "]---------------未启动成功，方法名设置错误！！！");
            return;
        } catch (SecurityException e) {
            e.printStackTrace();
        }

        if (method != null) {
            try {
                method.invoke(object, taskModelVo.getParam(),taskModelVo.getGameId());
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException  e) {
                flag = false;
                saveTaskLog(taskModelVo.getId(), taskModelVo.getName(), taskModelVo.getBeanClass(),flag,taskModelVo.getGameId(),"方法执行错误"+e.getMessage());
                e.printStackTrace();
            }
        }
        if(flag){
            log.info("任务名称 = [" + taskModelVo.getName() + "]----------启动成功");
            saveTaskLog(taskModelVo.getId(), taskModelVo.getName(), taskModelVo.getBeanClass(),flag,taskModelVo.getGameId(),"任务名称 = [" + taskModelVo.getName() + "]----------启动成功");
        }
    }

    /**
     * 保存定时任务运行记录日志
     * @param taskId
     * @param className
     * @param flag
     */
    public static void saveTaskLog(Integer taskId,String taskName,String className,Boolean flag,Integer gameId,String descript){
        //错误数据才存入进定时任务日志表
        if(!flag){
            TSysTaskLog tSysTaskLog = new TSysTaskLog();
            tSysTaskLog.setCreateTime(new Date());
            tSysTaskLog.setName(taskName);
            tSysTaskLog.setTaskId(taskId);
            tSysTaskLog.setGameId(gameId);
            tSysTaskLog.setState(flag);
            tSysTaskLog.setDescript(descript);
            synchronized(lock){
                logList.add(tSysTaskLog);
                if(logList.size()>20) {
                    sysTaskLogService.saveAll(logList);
                    logList.clear();//100条保存一次，然后清空
                    lock.notifyAll();
                }
            }
        }

    }

    /**
     * 添加任务,或者更新定时任务
     * @param taskModelVo
     * @throws SchedulerException
     */
    public static void addTask(TaskModelVo taskModelVo)  {
        log.debug("===========执行定时任务开始taskModelVo============");
        log.debug(taskModelVo.toString());
        log.debug("===========执行定时任务开始taskModelVo============");
        Scheduler scheduler = null;
        try {
            scheduler = StdSchedulerFactory.getDefaultScheduler();

            TriggerKey triggerKey = TriggerKey.triggerKey(taskModelVo.getName(), taskModelVo.getGroupName());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 不存在，创建一个
            if (null == trigger) {
                log.debug(scheduler + "...........................................add");

                Class clazz = taskModelVo.getTaskState() ? TaskFactory.class
                        : TaskFactoryDisallow.class;

                JobDetail taskDetail = newJob(clazz)
                        .withIdentity(taskModelVo.getName(), taskModelVo.getGroupName())
                        .build();

                taskDetail.getJobDataMap().put("taskModelVo", taskModelVo);//设置业务属性

                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                        .cronSchedule(taskModelVo.getCron());//设置时间精度

                trigger = newTrigger()
                        .withDescription(taskModelVo.getId().toString())
                        .startNow()
                        .withIdentity(taskModelVo.getName(), taskModelVo.getGroupName())
                        .withSchedule(scheduleBuilder)
                        .build();
                //加入到计划调度队列
                scheduler.scheduleJob(taskDetail, trigger);

                scheduler.start();

            } else {
                log.debug(scheduler + "...........................................updateLog");
                // Trigger已存在，那么更新相应的定时设置
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(taskModelVo.getCron());

                // 按新的cronExpression表达式重新构建trigger
                trigger = newTrigger().withIdentity(triggerKey).usingJobData("data", taskModelVo.getParam()).withSchedule(scheduleBuilder).build();

                // 按新的trigger重新设置job执行
                scheduler.rescheduleJob(triggerKey, trigger);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 移除一个任务(使用默认的任务组名，触发器名，触发器组名)
     * @param taskModelVo
     */
    public static void removeTask(TaskModelVo taskModelVo) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(taskModelVo.getName(), taskModelVo.getGroupName());
        JobKey jobKey = JobKey.jobKey(taskModelVo.getName(), taskModelVo.getGroupName());
        try {
            Trigger trigger = (Trigger) scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            scheduler.pauseTrigger(triggerKey);// 停止触发器
            scheduler.unscheduleJob(triggerKey);// 移除触发器
            scheduler.deleteJob(jobKey);// 删除任务
            log.info("移除任务:"+ taskModelVo.getName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 暂停一个任务
     * @param taskModelVo
     */
    public static void pauseTask(TaskModelVo taskModelVo) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobKey jobKey = JobKey.jobKey(taskModelVo.getName(), taskModelVo.getGroupName());
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }


    /**
     * 恢复一个任务
     * @param taskModelVo
     */
    public static void resumeTask(TaskModelVo taskModelVo) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobKey jobKey = JobKey.jobKey(taskModelVo.getName(), taskModelVo.getGroupName());
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 启动所有定时任务
     */
    public static void startJobs() throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭所有定时任务
     */
    public static void shutdownJobs() throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 立即运行一次
     * @param taskModelVo
     */
    public static void triggerTask(TaskModelVo taskModelVo) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobKey jobKey = JobKey.jobKey(taskModelVo.getName(), taskModelVo.getGroupName());
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取任务状态
     *      * 		NONE: 不存在
     *      * 		NORMAL: 正常
     *      * 		PAUSED: 暂停
     *      * 		COMPLETE:完成
     *      * 		ERROR : 错误
     *      * 		BLOCKED : 阻塞
     * @param taskModelVo
     * @return
     */
    public static String getTriggerState(TaskModelVo taskModelVo) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(taskModelVo.getName(), taskModelVo.getGroupName());
        String name = null;
        try {
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            name = triggerState.name();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return name;
    }

    /**
     * 获取最近8次执行时间
     * @param cron
     * @return
     */
    public static List<String> getRecentTriggerTime(String cron) {
        List<String> list = new ArrayList<String>();
        try {
            CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
            cronTriggerImpl.setCronExpression(cron);
            // 这个是重点，一行代码搞定
            List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 8);
            for (Date date : dates) {
                list.add(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
            }

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return list;
    }
    @Data
    public class abcs{
        private String did;
        private String name;
    }

    public static void main(String[] args) {

//        System.out.println(field2);
//        System.out.println(field2.getType());
//        try {
//            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//            //定义一个任务触发器，及其规则
//            Trigger trigger = newTrigger()
//                    .withIdentity("trigerr1","group1")
//                    .startNow()
//                    .withSchedule(
//                            simpleSchedule()
//                                    .withIntervalInSeconds(5)
//                                    .repeatForever()
//                    ).build();
//            //定义一个任务实例
//            JobDetail job = newJob(TaskFactory.class)
//                    .withIdentity("job1","group1")
//                    .build();
//            TaskModelVo model = new TaskModelVo();
//            model.setId(1L);
//            model.setSpringId("1");
//            model.setBeanClass(lottery.class.toString());
//            model.setMethodName("getPK10Result");
//            job.getJobDataMap().put("taskModel",model);
//            scheduler.scheduleJob(job,trigger);
//            scheduler.start();
//        } catch (SchedulerException e) {
//            e.printStackTrace();
//        }
    }
}



